package com.zuoyi.sqlSession;

import com.zuoyi.pojo.Configuration;
import com.zuoyi.pojo.MappedStatement;

import java.lang.reflect.*;
import java.util.List;

public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    public <E> List<E> selectList(String statementId, Object... params) throws Exception {
       /* MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        Executor simpleExecutor = new SimpleExecutor();
        //query方法也就是JDBC的执行只需要 1.数据库的配置信息 2.sql语句 3.以及参数。
        //通过statementId来获取 sql 信息
        List<E> list = simpleExecutor.query(configuration, mappedStatement, params);
        return list;*/
       return null;
    }

    public <T> T selectOne(String statementId, Object... params) throws Exception {
        List<Object> objects = selectList(statementId, params);
        if (objects.size() == 1) {
            return (T)objects.get(0);
        } else {
            throw new RuntimeException("查询结果为空或者返回结果过多");
        }
    }

    /**
     *添加接口
     *
     * 更好的实现方式应该是直接调用更新接口
     */
    public Integer insert(String statementId, Object... params) throws Exception{
        Integer i = update(statementId,params);
        return i;
    }
    /**
     * 更新接口
     */
    public Integer update(String statementId, Object... params) throws Exception{
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        Executor simpleExecutor = new SimpleExecutor();
        Integer i = simpleExecutor.executeUpdate(configuration, mappedStatement, params);
        return i;
    }

    /**
     * 删除接口
     */
    public Integer delete(String statementId, Object... params) throws Exception{
        Integer i = update(statementId,params);
        return i;
    }
    /**
     *getMapper()方法就是使用JDK动态代理为传过来的Dao接口，生成代理对象。
     * 再通过返回的代理对象，来调用方法完成具体的增删改操作
     *
     * 需要十分注意的一个点是:代理对象调用接口中任意方法，都会执行invoke()方法。
     */
    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        // 使用JDK动态代理来为Dao接口生成代理对象，并返回
        //需要三个参数 (1)类加载器 (2)Class数组 (3) 要实现的new InvocationHandler(){}
        Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            /**
             *invoke()方法的三个参数
             *          proxy:当前代理对象的引用
             *          method:当前被调用方法的引用
             *          args:传递的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 底层都还是去执行JDBC代码 //根据不同情况，来调用selctList或者selectOne
                // 准备参数 1：statmentid :sql语句的唯一标识：namespace.id= 接口全限定名.方法名
                // 方法名：findAll
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();//获取该方法所在类的字节码对象getDeclaringClass()

                String statementId = className+"."+methodName;

                // 准备参数2：params:args
                // 获取被调用方法的返回值类型
                Type genericReturnType = method.getGenericReturnType();
                // 判断是否进行了 泛型类型参数化  此时的情况是如果有泛型，那么返回的是一个集合就是查询所有，如果没有返回的是实体是查询单个
                if(genericReturnType instanceof ParameterizedType){
                    List<Object> objects = selectList(statementId, args);
                    return objects;
                } else {
                    //如果返回的结果集不是一个泛型，我们就通过MappedStatement对象中的SQL语句进行以什么开头来进行判断
                    MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                    //为避免SQL大小写差异统一将SQL语句转换成小写  → 通过sql语句的起始内容来判断执行的增加、修改、还是删除操作
                    String sql = mappedStatement.getSql().toLowerCase();
                    if(sql.startsWith("delete")){
                        return delete(statementId,args);
                    } else if(sql.startsWith("select")){
                        return selectOne(statementId,args);
                    } else if(sql.startsWith("insert")){
                        return insert(statementId,args);
                    }else {
                        return update(statementId,args);
                    }
                }
            }
        });

        return (T) proxyInstance;
    }

}
